package com.fc.push.handler.pub.exec.task;

import java.util.List;
import java.util.concurrent.TimeUnit;

import com.fc.push.exception.ExcetionLevelConst;
import com.fc.push.exception.MServerException;
import com.fc.push.logger.Applogger;

import javapns.communication.exceptions.CommunicationException;
import javapns.communication.exceptions.KeystoreException;
import javapns.devices.Device;
import javapns.devices.Devices;
import javapns.devices.exceptions.InvalidDeviceTokenFormatException;
import javapns.devices.implementations.basic.BasicDevice;
import javapns.notification.AppleNotificationServer;
import javapns.notification.Payload;
import javapns.notification.PushNotificationManager;
import javapns.notification.PushedNotification;
import javapns.notification.PushedNotifications;

public class APNsPublishTask implements Runnable {

	private List<Device> devices = null;
	private Payload payload = null;
	private PushNotificationManager notificationManager = null;
	private PushedNotifications pushedNotifications = null;
	private AppleNotificationServer server = null;

	private Object[] tokens = null;

	private int sleepBetweenNotifications = 0;
	// private int maxNotificationsPerConnection = 0;

	private MODE mm = MODE.LIST;

	public APNsPublishTask(AppleNotificationServer server, List<String> tokens, Payload payload) {
		this.devices = Devices.asDevices(tokens);
		this.payload = payload;
		this.server = server;
		notificationManager = new PushNotificationManager();
		pushedNotifications = new PushedNotifications();
		pushedNotifications.setMaxRetained(this.devices.size());
	}

	public APNsPublishTask(AppleNotificationServer server, Payload payload, Object... tokens) {
		this.tokens = tokens;
		this.payload = payload;
		this.server = server;
		notificationManager = new PushNotificationManager();
		pushedNotifications = new PushedNotifications();
		pushedNotifications.setMaxRetained(this.tokens.length);

		mm = MODE.ARRAY;
	}

	public static enum MODE {
		LIST, ARRAY
	}

	@Override
	public void run() {
		switch (mm) {
		case LIST:
			runList();
			break;
		case ARRAY:
			runArray();
			break;
		default:
			break;
		}
	}

	/**
	 * 通过列表发送
	 */
	private void runList() {

		if (devices == null) {
			throw new MServerException(Thread.currentThread().getName() + "init devices failed");
		}
		try {
			notificationManager.initializeConnection(this.server);
			
			Applogger.instance().getLogger().info("payload:" + payload.getPayload().toString() + "message length:"
					+ payload.getPayload().toString().getBytes().length);
			for (Device device : devices) {
				PushedNotification notification = null;
				notification = notificationManager.sendNotification(device, payload, false, device.hashCode());
				pushedNotifications.add(notification);
				if (sleepBetweenNotifications > 0)
					TimeUnit.SECONDS.sleep(sleepBetweenNotifications);

				// if (i != 0 && i % maxNotificationsPerConnection == 0) {
				// if (listener != null)
				// listener.eventConnectionRestarted(this);
				// notificationManager.restartConnection(server);
				// }
			}

			/**
			 * 发送失败的做第二次重发
			 */
			List<PushedNotification> failedNotifications = null;
			if ((failedNotifications = PushedNotification.findFailedNotifications(pushedNotifications)).size() > 0) {
				pushedNotifications.clear();
				for (PushedNotification notification : failedNotifications) {
					notificationManager.sendNotification(notification.getDevice(), notification.getPayload());
					pushedNotifications.add(notification);
				}
			}
			
			if ((failedNotifications = PushedNotification.findFailedNotifications(pushedNotifications)).size() > 0){
				for (PushedNotification notification : failedNotifications) {
					Applogger.instance().getLogger().info(notification.toString());
					Applogger.instance().getLogger().info("failed payload:" + payload.getPayload().toString() + " token:"
							+ notification.getDevice().getToken());
					
					
				}
			}
			
			notificationManager.stopConnection();
		} catch (CommunicationException e) {
			throw new MServerException(e, e.getMessage(), ExcetionLevelConst.EXCEPTION_LEVEL_2);
		} catch (KeystoreException e) {
			Applogger.instance().getLogger().error(e.getMessage(), e);
		} catch (InterruptedException e) {
			Applogger.instance().getLogger().error(e.getMessage(), e);
		}
	}

	/**
	 * 数组发送
	 */
	private void runArray() {

		if (tokens == null) {
			throw new MServerException(Thread.currentThread().getName() + "tokens is empty");
		}

		try {
			notificationManager.initializeConnection(this.server);
			
			Applogger.instance().getLogger().info("payload:" + payload.getPayload().toString() + "message length:"
					+ payload.getPayload().toString().getBytes().length);
			for (Object object : tokens) {
				PushedNotification notification = null;
				Device device = new BasicDevice(object.toString());
				notification = notificationManager.sendNotification(device, payload, false, device.hashCode());
				pushedNotifications.add(notification);
				
				if (sleepBetweenNotifications > 0)
					TimeUnit.SECONDS.sleep(sleepBetweenNotifications);
				// if (i != 0 && i % maxNotificationsPerConnection == 0) {
				// if (listener != null)
				// listener.eventConnectionRestarted(this);
				// notificationManager.restartConnection(server);
				// }
			}

			/**
			 * 发送失败的做第二次重发
			 */
			List<PushedNotification> failedNotifications = null;
			if ((failedNotifications = PushedNotification.findFailedNotifications(pushedNotifications)).size() > 0) {
				pushedNotifications.clear();
				for (PushedNotification notification : failedNotifications) {
					notificationManager.sendNotification(notification.getDevice(), notification.getPayload());
					pushedNotifications.add(notification);
				}
			}
			
			if ((failedNotifications = PushedNotification.findFailedNotifications(pushedNotifications)).size() > 0){
				for (PushedNotification notification : failedNotifications) {
					Applogger.instance().getLogger().info(notification.toString());
					Applogger.instance().getLogger().info("failed payload:" + payload.getPayload().toString() + " token:"
							+ notification.getDevice().getToken());
				}
			}
			
			notificationManager.stopConnection();
		} catch (CommunicationException e) {
			throw new MServerException(e, e.getMessage(),ExcetionLevelConst.EXCEPTION_LEVEL_2);
		} catch (KeystoreException e) {
			throw new MServerException(e, e.getMessage());
		} catch (InvalidDeviceTokenFormatException e) {
			throw new MServerException(e, e.getMessage());
		} catch (InterruptedException e) {
			throw new MServerException(e, e.getMessage());
		}
	}
}